package com.azhoucode.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class RedisCacheUtils {

    private static final Logger logger = LoggerFactory.getLogger(RedisCacheUtils.class);

    private static JedisPool jedisPool;

    private RedisCacheUtils() {}

    public static void setPool(JedisPool jedisPool) {
        RedisCacheUtils.jedisPool = jedisPool;
    }

    /**
     * 从redis中取值
     *
     * @param key   键
     * @param clazz 值得类型
     * @param <T>   值的泛型
     * @return 返回存入redis的，经过json反序列化后数据
     */
    public static <T> T get(String key, Class<T> clazz) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            String value = redis.get(key);
            return stringToBean(value, clazz);
        } catch (Exception e) {
            logger.error("从redis读数据{get}操作发生异常", e);
            return null;
        } finally {
            release(redis);
        }
    }

    /**
     * 从redis中取值
     *
     * @param key 键
     * @return 返回存入redis的，经过json反序列化后数据
     */
    public static String get(String key) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            return redis.get(key);
        } catch (Exception e) {
            logger.error("从redis读数据{get}操作发生异常", e);
            return null;
        } finally {
            release(redis);
        }
    }

    /**
     * 为key设置value，不过期
     *
     * @param key 键
     * @param <T> 值的泛型
     * @return 设置成功返回true
     */
    public static <T> Boolean set(String key, T value) {
        return set(key, value, -1);
    }

    /**
     * 为key设置value，并指定key的过期时间
     *
     * @param key 键
     * @param <T> 值的泛型
     * @return 设置成功返回true
     */
    public static <T> Boolean set(String key, T value, int expireTimeSeconds) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            String str = beanToString(value);
            if (str == null) {
                return false;
            }
            if (expireTimeSeconds <= 0) {
                redis.set(key, str);
            } else {
                redis.setex(key, expireTimeSeconds, str);
            }
            return true;
        } catch (Exception e) {
            logger.error("从redis写数据{set}操作发生异常", e);
            return null;
        } finally {
            release(redis);
        }
    }

    /**
     * 根据key删除value
     *
     * @param key 键
     * @return 删除成功返回true
     */
    public static Boolean delete(String key) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            Long del = redis.del(key);
            return del > 0L;
        } catch (Exception e) {
            logger.error("从redis写数据{delete}操作发生异常", e);
            return null;
        } finally {
            release(redis);
        }
    }

    /**
     * 判断redis是否存在key
     *
     * @param key 键
     * @return 存在key返回true
     */
    public static boolean exists(String key) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            Boolean exists = redis.exists(key);
            return exists != null && exists;
        } catch (Exception e) {
            logger.error("从redis读数据{exists}操作发生异常", e);
            return false;
        } finally {
            release(redis);
        }
    }

    /**
     * redis中key自增
     *
     * @param key 键
     * @return 自增后的数据
     */
    public static Long incr(String key) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            return redis.incr(key);
        } catch (Exception e) {
            logger.error("从redis写数据{incr}操作发生异常", e);
            return null;
        } finally {
            release(redis);
        }
    }

    /**
     * 指定key在多少秒后过期
     *
     * @param key               键
     * @param expireTimeSeconds 过期时间，单位秒
     */
    public static void expireAfter(String key, int expireTimeSeconds) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            redis.expire(key, expireTimeSeconds);
        } catch (Exception e) {
            logger.error("从redis写数据{expire}操作发生异常", e);
        } finally {
            release(redis);
        }
    }

    /**
     * 获取hash结构中field的属性值
     *
     * @param key   hash键名
     * @param field hash字段名
     * @return hash字段对应的值
     */
    public static String hget(String key, String field) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            return redis.hget(key, field);
        } catch (Exception e) {
            logger.error("从redis读数据{hget}操作发生异常", e);
            return null;
        } finally {
            release(redis);
        }
    }

    /**
     * 获取hash结构中所有field的属性键值对
     *
     * @param key hash键名
     * @return hash结构
     */
    public static void hmset(String key, Map<String, String> map) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            redis.hmset(key, map);
        } catch (Exception e) {
            logger.error("从redis读数据{hgetAll}操作发生异常", e);
        } finally {
            release(redis);
        }
    }

    public static void hmset(String key, Map<String, String> map, int expireTimeSeconds) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            redis.hmset(key, map);
            if (expireTimeSeconds > 0) {
                expireAfter(key, expireTimeSeconds);
            }
        } catch (Exception e) {
            logger.error("从redis读数据{hgetAll}操作发生异常", e);
        } finally {
            release(redis);
        }
    }

    /**
     * 获取hash结构中所有field的属性键值对
     *
     * @param key hash键名
     * @return hash结构
     */
    public static Map<String, String> hgetAll(String key) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            return redis.hgetAll(key);
        } catch (Exception e) {
            logger.error("从redis读数据{hgetAll}操作发生异常", e);
            return null;
        } finally {
            release(redis);
        }
    }

    /**
     * 获取hash结构中field的属性值
     *
     * @param key        hash键名
     * @param field      hash字段名
     * @param fieldValue hash字段值
     */
    public static void hset(String key, String field, String fieldValue) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            redis.hset(key, field, fieldValue);
        } catch (Exception e) {
            logger.error("从redis写数据{hset}操作发生异常", e);
        } finally {
            release(redis);
        }
    }

    /**
     * 获取hash结构中field的属性值
     *
     * @param key        hash键名
     * @param field      hash字段名
     * @param fieldValue hash字段值
     */
    public static void hset(String key, String field, String fieldValue, int expireTimeSeconds) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            hset(key, field, fieldValue);
            if (expireTimeSeconds > 0) {
                expireAfter(key, expireTimeSeconds);
            }
        } catch (Exception e) {
            logger.error("从redis写数据{hset}操作发生异常", e);
        } finally {
            release(redis);
        }
    }

    /**
     * 往redis sortedSet数据结构中存入数据
     *
     * @param key    key值
     * @param score  key的打分
     * @param member 值
     */
    public static void zadd(String key, double score, String member) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            redis.zadd(key, score, member);
        } catch (Exception e) {
            logger.error("从redis写数据{zadd}操作发生异常", e);
        } finally {
            release(redis);
        }
    }

    /**
     * 批量删除
     *
     * @param key    健
     * @param member 指定数据
     */
    public static void zremove(String key, String... member) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            redis.zrem(key, member);
        } catch (Exception e) {
            logger.error("从redis写数据{zremove}操作发生异常", e);
        } finally {
            release(redis);
        }
    }

    /**
     * 批量删除
     *
     * @param key   健
     * @param start 打分起始
     * @param end   打分截止
     * @return 返回被删除的数量
     */
    public static Long zremrangeByScore(String key, long start, long end) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            return redis.zremrangeByScore(key, start, end);
        } catch (Exception e) {
            logger.error("从redis写数据{zremrangeByScore}操作发生异常", e);
            return null;
        } finally {
            release(redis);
        }
    }

    /**
     * 统计集合中元素的数量
     *
     * @param key 键
     * @return 元素数量
     */
    public static Long zcard(String key) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            return redis.zcard(key);
        } catch (Exception e) {
            logger.error("从redis读数据{zcard}操作发生异常", e);
            return null;
        } finally {
            release(redis);
        }
    }

    /**
     * 用下标的方式从redis取出数据，并按照打分数据从低到高升序排序
     *
     * @param key   key
     * @param start 下标起始
     * @param end   下标截止
     * @return 返回 {@link java.util.HashSet}
     */
    public static Set<String> zrange(String key, long start, long end) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            return redis.zrange(key, start, end);

        } catch (Exception e) {
            logger.error("从redis读数据{zrange}操作发生异常", e);
            return null;
        } finally {
            release(redis);
        }
    }

    /**
     * 用下标的方式从redis取出数据，并按照打分数据从高到低降序排序
     *
     * @param key   key
     * @param start 下标起始
     * @param end   下标截止
     * @return 返回 {@link java.util.HashSet}
     */
    public static Set<String> zrevrange(String key, long start, long end) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            return redis.zrevrange(key, start, end);
        } catch (Exception e) {
            logger.error("从redis读数据{zrevrange}操作发生异常", e);
            return null;
        } finally {
            release(redis);
        }
    }

    /**
     * 往队列左边push数据
     *
     * @param key   key值
     * @param value value值
     * @return 操作成功返回true，否则返回false
     */
    public static boolean lpush(String key, String value) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.lpush(key, value);
            return true;
        } catch (Exception e) {
            logger.error("从redis写数据{lpush}操作发生异常", e);
            return false;
        } finally {
            release(jedis);
        }
    }

    /**
     * 从redis队列右边移除并取出元素
     *
     * @param key   key值
     * @param clazz value值得class类型
     * @param <T>   泛型
     * @return 返回元素值序列化后的bean
     */
    public static <T> T rpop(String key, Class<T> clazz) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String pop = jedis.rpop(key);
            return stringToBean(pop, clazz);
        } catch (Exception e) {
            logger.error("从redis写数据{rpop}操作发生异常", e);
            return null;
        } finally {
            release(jedis);
        }
    }

    /**
     * 从redis队列中获取元素，并不移除元素
     *
     * @param key   key值
     * @param start 起始
     * @param end   截止
     * @return {@link ArrayList}
     */
    public static List<String> lrange(String key, long start, long end) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.lrange(key, start, end);
        } catch (Exception e) {
            logger.error("从redis读数据{lrange}操作发生异常", e);
            return null;
        } finally {
            release(jedis);
        }
    }

    /**
     * 列表sorted set中的数据，返回打分数据
     *
     * @param key   key值
     * @param start 起始
     * @param end   截止
     * @return {@link ArrayList}
     */
    public static Set<Tuple> zrangeByScoreWithScores(String key, long start, long end) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.zrangeByScoreWithScores(key, start, end);
        } catch (Exception e) {
            logger.error("从redis读数据{zrangeByScoreWithScores}操作发生异常", e);
            return null;
        } finally {
            release(jedis);
        }
    }

    /**
     * 按照打分的数据升序排序，返回Tuple对象，包含元素和元素的得分
     *
     * @param key    key值
     * @param min    查询的最小打分数
     * @param max    查询的最大打分数
     * @param offset 偏移量
     * @param count  查询总数
     * @return {@link java.util.HashSet}
     */
    public static Set<Tuple> zrangeByScoreWithScores(String key, double min, double max, int offset, int count) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.zrangeByScoreWithScores(key, min, max, offset, count);
        } catch (Exception e) {
            logger.error("从redis读数据{zrangeByScoreWithScores}操作发生异常", e);
            return null;
        } finally {
            release(jedis);
        }
    }

    /**
     * 按照打分的数据降序排序，返回Tuple对象，包含元素和元素的得分
     *
     * @param key    key值
     * @param max    查询的最大打分数
     * @param min    查询的最小打分数
     * @param offset 偏移量
     * @param count  查询总数
     * @return {@link java.util.HashSet}
     */
    public static Set<Tuple> zrevrangeByScoreWithScores(String key, double max, double min, int offset, int count) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.zrevrangeByScoreWithScores(key, max, min, offset, count);
        } catch (Exception e) {
            logger.error("从redis读数据{zrevrangeByScoreWithScores}操作发生异常", e);
            return null;
        } finally {
            release(jedis);
        }
    }

    /**
     * 按照打分的数据降序排序，返回Tuple对象，包含元素和元素的得分
     *
     * @param key key值
     * @param max 查询的最大打分数
     * @param min 查询的最小打分数
     * @return {@link java.util.HashSet}
     */
    public static Set<Tuple> zrevrangeByScoreWithScores(String key, double max, double min) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.zrevrangeByScoreWithScores(key, max, min);
        } catch (Exception e) {
            logger.error("从redis读数据{zrevrangeByScoreWithScores}操作发生异常", e);
            return null;
        } finally {
            release(jedis);
        }
    }

    /**
     * 查询返回符合某一表达式规则的key的列表集合，例如：user:*，返回以user:开头的key的列表
     *
     * @param keyPattern 表达式
     * @return 列表集合
     */
    public static List<String> scan(String keyPattern) {
        Jedis redis = null;
        try {
            redis = jedisPool.getResource();
            ScanParams scanParams = new ScanParams();
            scanParams.match(keyPattern);
            scanParams.count(Integer.MAX_VALUE);
            ScanResult<String> scanResult = redis.scan(ScanParams.SCAN_POINTER_START, scanParams);
            return scanResult.getResult();
        } catch (Exception e) {
            logger.error("从redis读数据{scan}操作发生异常", e);
            return new ArrayList<>();
        } finally {
            release(redis);
        }
    }

    /**
     * java bean转字符串
     *
     * @param value 值
     * @param <T>   泛型
     * @return json工具序列化
     */
    private static <T> String beanToString(T value) {
        if (value == null) {
            return null;
        }
        Class<?> clazz = value.getClass();
        if ((clazz == Integer.TYPE) || (clazz == Integer.class) || (clazz == Long.TYPE) || (clazz == Long.class)) {
            return String.valueOf(value);
        } else if (clazz == String.class) {
            return (String)value;
        }
        return JsonUtils.objectToJson(value);
    }

    /**
     * 字符串转java bean
     *
     * @param value 值
     * @param clazz 类型
     * @param <T>   泛型
     * @return json工具反序列化
     */
    @SuppressWarnings("unchecked")
    private static <T> T stringToBean(String value, Class<T> clazz) {
        if (value == null || clazz == null) {
            return null;
        }
        if ((clazz == Integer.TYPE) || (clazz == Integer.class)) {
            return (T)Integer.valueOf(value);
        }
        if ((clazz == Long.TYPE) || (clazz == Long.class)) {
            return (T)Long.valueOf(value);
        }
        if (clazz == String.class) {
            return (T)value;
        }
        return JsonUtils.jsonToObject(value, clazz);
    }

    /**
     * 释放链接
     *
     * @param jedis {@link Jedis}
     */
    private static void release(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }
}
